﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using VSIXUtilities.DataObjects;
using VSIXUtilities.GUI;
using VSLangProj;
using Microsoft.VisualStudio.Shell.Interop;
using System.Linq;
using CodeDocumentationSynchronizer.CodDocSync.Helpers;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.VisualStudio.Designer.Interfaces;
using System.Runtime.InteropServices;
using System.CodeDom.Compiler;
using System.Collections;
using System.CodeDom;

namespace VSIXUtilities.Helpers.Extensions
{
    public static class GeneralExtensions
    {
        public static string GetAssemblyPath(this Project ProjectInstance, Configuration ConfigurationInstance)
        {
            if (ConfigurationInstance is ProjectConfigurationProperties)
            {
                return ((ProjectConfigurationProperties)ConfigurationInstance).OutputPath;
            }
            Property PropertyOutputPath = ConfigurationInstance.Properties.ItemByName("OutputPath");
            if (PropertyOutputPath == null)
            {
                return string.Empty;
            }
            string stringOutputFileName = ProjectInstance.GetOutputFileName(ConfigurationInstance);
            if (string.IsNullOrEmpty(stringOutputFileName))
            {
                return string.Empty;
            }
            string stringOutputPath = Convert.ToString(PropertyOutputPath.Value);
            /*
            The output folder can have these patterns:
                1) "drive:\folder"
                2) "\\server\folder"
                3) "..\..\folder"
                4) "folder"
            */
            if (Path.IsPathRooted(stringOutputPath))
            {
                //This is the case 1: "drive:\folder"
                return Path.Combine(stringOutputPath, stringOutputFileName);
            }
            else if (stringOutputPath.StartsWith(Path.DirectorySeparatorChar.ToString() + Path.DirectorySeparatorChar.ToString()))
            {
                //This is the case 2: "\\server\folder"
                return Path.Combine(stringOutputPath, stringOutputFileName);
            }
            //else if(stringOutputPath.Length >= 2 && stringOutputPath[1] == Path.VolumeSeparatorChar)
            //{
            //    //This is the case 1: "drive:\folder"
            //    return Path.Combine(stringOutputPath, stringOutputFileName);
            //}
            else if (stringOutputPath.IndexOf("..\\") != -1)
            {
                //This is the case 3: "..\..\folder"
                return Path.Combine(Helper.ConvertToAbsolute(ProjectInstance.FullName, stringOutputPath), stringOutputFileName);
            }
            else
            {
                return string.Format("{0}\\{1}{2}", Path.GetDirectoryName(ProjectInstance.FullName), PropertyOutputPath.Value, stringOutputFileName);
            }
        }
        public static Property ItemByName(this Properties PropertiesInstance, string Name)
        {
            foreach (Property PropertyInstance in PropertiesInstance)
            {
                if (PropertyInstance.Name == Name)
                {
                    return PropertyInstance;
                }
            }
            return null;
        }
        public static string GetOutputFileName(this Project ProjectInstance, Configuration ConfigurationInstance)
        {
            Property PropertyOutputFileName = ProjectInstance.Properties.ItemByName("OutputFileName");
            if (PropertyOutputFileName == null)
            {
                Property PropertyItemName = ProjectInstance.Properties.ItemByName("ItemName");
                if (PropertyItemName == null)
                {
                    return string.Empty;
                }
                Property PropertyConfigurationType = ConfigurationInstance.Properties.ItemByName("ConfigurationType");
                if (PropertyConfigurationType == null)
                {
                    return string.Empty;
                }
                switch (Convert.ToInt16(PropertyConfigurationType.Value))
                {
                    case 1:
                        return string.Format("{0}.exe", PropertyItemName.Value);
                    case 2:
                        return string.Format("{0}.dll", PropertyItemName.Value);
                    case 4:
                        return string.Format("{0}.lib", PropertyItemName.Value);
                    default:
                        return string.Empty;
                }
            }
            return Convert.ToString(PropertyOutputFileName.Value);
        }
        public static string[] ToLines(this string stringInstance)
        {
            //return stringInstance.Trim().Split(new string[] { "\r" }, StringSplitOptions.RemoveEmptyEntries);
            return Regex.Split(stringInstance.TrimEnd(), "[\\r][\\n]|[\\r]|[\\n]", RegexOptions.Compiled);
        }
        public static bool ContainsEx(this List<AssemblyName> list, AssemblyName Name)
        {
            foreach (AssemblyName AssemblyNameInstance in list)
            {
                if (AssemblyNameInstance.FullName == Name.FullName)
                {
                    return true;
                }
            }
            return false;
        }
        public static bool ContainsEx(this List<AssemblyName> list, IVsHierarchyWrapper Rf)
        {
            foreach (AssemblyName AssemblyNameInstance in list)
            {
                if (AssemblyNameInstance.Name.ToLower() == Rf.Name.ToLower())
                {
                    return true;
                }
            }
            return false;
        }
        public static bool ContainsEx(this List<string> list, IVsHierarchyWrapper Rf)
        {
            foreach (string AssemblyNameInstance in list)
            {
                if (AssemblyNameInstance == Rf.Name)
                {
                    return true;
                }
            }
            return false;
        }
        public static void AddRangeEx(this List<AssemblyName> list, IEnumerable<AssemblyName> collection)
        {
            foreach (AssemblyName AssemblyNameInstance in collection)
            {
                if (!list.ContainsEx(AssemblyNameInstance))
                {
                    list.Add(AssemblyNameInstance);
                }
            }
        }
        public static void AddRangeEx(this List<string> list, IEnumerable<string> collection)
        {
            foreach (string AssemblyNameInstance in collection)
            {
                if (!list.Contains(AssemblyNameInstance))
                {
                    list.Add(AssemblyNameInstance);
                }
            }
        }
        public static string GetTypeName(this Reference ReferenceInstance)
        {
            if (ReferenceInstance.Type == prjReferenceType.prjReferenceTypeActiveX)
            {
                return "COM";
            }
            else
            {
                return "Assembly";
            }
        }
        public static List<ToggleButtonTreeViewItem<CheckBox>> GetCheckBoxItems(this TreeView TreeViewInstance)
        {
            List<ToggleButtonTreeViewItem<CheckBox>> Result = new List<ToggleButtonTreeViewItem<CheckBox>>();
            foreach (object objectItem in TreeViewInstance.Items)
            {
                Result.Add((ToggleButtonTreeViewItem<CheckBox>)objectItem);
            }
            return Result;
        }
        public static IVsHierarchyWrapper GetIVsHierarchyWrapper(this ToggleButtonTreeViewItem<CheckBox> CheckBoxTreeViewItemInstance)
        {
            return (IVsHierarchyWrapper)CheckBoxTreeViewItemInstance.Tag;
        }
        public static string ApplyDocumentationStyle(this XmlNode XmlNodeInstance, string Identation)
        {
            return Identation + "/// " + Regex.Replace(XmlNodeInstance.OuterXml.TrimEnd(), "(?<LineBr>[\\r][\\n]|[\\r]|[\\n])\\s?", string.Format(@"$1{0}/// ", Identation), RegexOptions.Compiled).Replace(" />", "/>");
        }

        public static string[] ReadAllLines(this TextReader TextReaderInstance)
        {
            List<string> list = new List<string>();
            string str;
            while ((str = TextReaderInstance.ReadLine()) != null)
            {
                list.Add(str);
            }
            return list.ToArray();
        }

        public static void WriteAllLines(this TextWriter TextWriterInstance, IEnumerable<string> contents)
        {
            foreach (string str in contents)
            {
                TextWriterInstance.WriteLine(str);
            }
        }

        public static Position GetPosition(this TextSelection TextSelectionInstance)
        {
            return Position.FromVirtualPoint(TextSelectionInstance.ActivePoint);
        }

        public static void MoveToPosition(this TextSelection TextSelectionInstance, Position Location)
        {
            TextSelectionInstance.MoveToLineAndOffset(Location.Line, Location.LineCharOffset);
        }

        public static void MoveToLine(this TextSelection TextSelectionInstance, int Line)
        {
            TextSelectionInstance.MoveToLineAndOffset(Line, TextSelectionInstance.ActivePoint.LineCharOffset);
        }

        public static void DeleteLine(this EditPoint EditPointIInstance)
        {
            EditPointIInstance.MoveToLineAndOffset(EditPointIInstance.Line, 1);
            EditPointIInstance.Delete(EditPointIInstance.LineLength + 1);
        }

        public static string GetDocCommentNotation(string extension)
        {
            if (extension.EndsWith(".cs") || extension.EndsWith(".cpp") || extension.EndsWith(".h") || extension.EndsWith(".idl") || extension.EndsWith(".jsl"))
            {
                return "/// ";
            }
            else if (extension.EndsWith(".vb"))
            {
                return "''' ";
            }
            else
            {
                throw new Exception("Unrecognized file type.");
            }
        }

        public static bool CanBeDocCommented(this IVsHierarchyWrapper IVsHierarchyWrapperInstance)
        {
            return IVsHierarchyWrapperInstance.IsProjectItem && Cache.LanguageSpecs.IsSupported(Path.GetExtension(IVsHierarchyWrapperInstance.Name));
        }

        public static string GetUntrimmableInstance(this string stringInstance)
        {
            return string.Format("\a{0}\a", stringInstance.Replace(Environment.NewLine, Environment.NewLine + "\a"));
        }

        public static string RemoveLineBreaks(this string stringInstance)
        {
            return stringInstance.Replace(Environment.NewLine, string.Empty);
        }

        public static Image GetImage(this ImageSource ImageSourceInstance)
        {
            return new Image()
            {
                Source = ImageSourceInstance
            };
        }

        public static IVsHierarchy ToIVsHierarchy(this Project project)
        {
            IVsSolution IVsSolutionInstance = ServiceProvider.GlobalProvider.GetService(typeof(SVsSolution)) as IVsSolution;
            IVsHierarchy Result;
            IVsSolutionInstance.GetProjectOfUniqueName(project.FullName, out Result);
            return Result;
        }

        public static ICodeParser GetICodeParser(this IVSMDCodeDomProvider IVSMDCodeDomProviderInstance)
        {
            ICodeParser ICodeParserResult = null;
            if (IVSMDCodeDomProviderInstance != null)
            {
                object innerProvider = IVSMDCodeDomProviderInstance.CodeDomProvider;
                if (innerProvider != null)
                {
                    Type innerProviderType = innerProvider.GetType();
                    PropertyInfo propertyInfo = innerProviderType.GetProperty("CodeDomProviders", BindingFlags.Instance | BindingFlags.NonPublic);
                    if (propertyInfo != null)
                    {
                        MethodInfo methodInfo = propertyInfo.GetGetMethod(true);
                        ArrayList innerProviders = methodInfo.Invoke(innerProvider, null) as ArrayList;
                        innerProvider = (innerProviders != null && innerProviders.Count > 0) ? innerProviders[0] : null;
                        innerProviderType = innerProvider.GetType();
                        propertyInfo = innerProviderType.GetProperty("CodeParser", BindingFlags.NonPublic | BindingFlags.Instance);

                        if (propertyInfo != null)
                        {
                            ICodeParserResult = propertyInfo.GetValue(innerProvider, null) as ICodeParser;
                        }
                    }
                }
            }
            return ICodeParserResult;
        }

        public static IVSMDCodeDomProvider GetIVSMDCodeDomProvider(this Microsoft.VisualStudio.OLE.Interop.IServiceProvider IServiceProviderInstance)
        {
            IVSMDCodeDomProvider IVSMDCodeDomProviderResult = null;
            Guid serviceGuid = typeof(SVSMDCodeDomProvider).GUID;
            Guid interfaceGuid = typeof(IVSMDCodeDomProvider).GUID;
            IntPtr servicePointer = IntPtr.Zero;
            int result = -1;

            result = IServiceProviderInstance.QueryService(ref serviceGuid, ref interfaceGuid, out servicePointer);
            if (result == 0)
            {
                IVSMDCodeDomProviderResult = Marshal.GetObjectForIUnknown(servicePointer) as IVSMDCodeDomProvider;
            }
            return IVSMDCodeDomProviderResult;
        }

        public static ICodeParser GetICodeParser(this Microsoft.VisualStudio.OLE.Interop.IServiceProvider IServiceProviderInstance)
        {
            IVSMDCodeDomProvider IVSMDCodeDomProviderInstance = IServiceProviderInstance.GetIVSMDCodeDomProvider();
            ICodeParser ICodeParserResult = null;
            if (IVSMDCodeDomProviderInstance != null)
            {
                ICodeParserResult = IVSMDCodeDomProviderInstance.GetICodeParser();
            }
            return ICodeParserResult;
        }

        public static string GetDataString(this IDictionary IDictionaryInstance)
        {
            StringBuilder StringBuilderResult = new StringBuilder();
            foreach (object ObjectKey in IDictionaryInstance.Keys)
            {
                StringBuilderResult.AppendFormat("{0}:\t\t{1}{2}", ObjectKey, IDictionaryInstance[ObjectKey], Environment.NewLine);
            }
            return StringBuilderResult.ToString();
        }

        public static string GetDataString(this Properties PropertiesInstance)
        {
            StringBuilder StringBuilderResult = new StringBuilder();
            foreach (Property PropertyInstance in PropertiesInstance)
            {
                StringBuilderResult.AppendFormat("{0}:\t\t{1}{2}", PropertyInstance.Name, PropertyInstance.Value, Environment.NewLine);
            }
            return StringBuilderResult.ToString();
        }

        public static CodeElement ToCodeElement(this CodeObject CodeObjectInstance)
        {
            return (CodeElement)CodeObjectInstance.UserData[typeof(CodeElement)];
        }
    }
}
